import EvmKit
import Foundation
import UniswapKit

class UniswapV3Module {
    private let tradeService: UniswapV3TradeService
    private let allowanceService: SwapAllowanceService
    private let pendingAllowanceService: SwapPendingAllowanceService
    private let service: UniswapV3Service

    init?(dex: SwapModule.Dex, dataSourceState: SwapModule.DataSourceState) {
        guard let evmKit = try? Core.shared.evmBlockchainManager.evmKitManager(blockchainType: dex.blockchainType).evmKitWrapper?.evmKit else {
            return nil
        }

        guard let swapKit = try? UniswapKit.KitV3.instance(dexType: dex.provider.dexType),
              let rpcSource = Core.shared.evmSyncSourceManager.httpSyncSource(blockchainType: dex.blockchainType)?.rpcSource
        else {
            return nil
        }

        let uniswapRepository = UniswapV3Provider(swapKit: swapKit, evmKit: evmKit, rpcSource: rpcSource)

        tradeService = UniswapV3TradeService(
            uniswapProvider: uniswapRepository,
            state: dataSourceState,
            evmKit: evmKit
        )
        allowanceService = SwapAllowanceService(
            spenderAddress: uniswapRepository.routerAddress,
            adapterManager: Core.shared.adapterManager,
            evmKit: evmKit
        )
        pendingAllowanceService = SwapPendingAllowanceService(
            spenderAddress: uniswapRepository.routerAddress,
            adapterManager: Core.shared.adapterManager,
            allowanceService: allowanceService
        )
        service = UniswapV3Service(
            dex: dex,
            tradeService: tradeService,
            allowanceService: allowanceService,
            pendingAllowanceService: pendingAllowanceService,
            adapterManager: Core.shared.adapterManager
        )
    }
}

extension UniswapV3Module: ISwapProvider {
    var dataSource: ISwapDataSource {
        let allowanceViewModel = SwapAllowanceViewModel(errorProvider: service, allowanceService: allowanceService, pendingAllowanceService: pendingAllowanceService)
        let viewModel = UniswapV3ViewModel(
            service: service,
            tradeService: tradeService,
            switchService: AmountTypeSwitchService(userDefaultsStorage: Core.shared.userDefaultsStorage, useLocalStorage: false),
            allowanceService: allowanceService,
            pendingAllowanceService: pendingAllowanceService,
            currencyManager: Core.shared.currencyManager,
            viewItemHelper: SwapViewItemHelper()
        )

        return UniswapV3DataSource(
            viewModel: viewModel,
            allowanceViewModel: allowanceViewModel
        )
    }

    var settingsDataSource: ISwapSettingsDataSource? {
        UniswapSettingsModule.dataSource(settingProvider: tradeService, showDeadline: false)
    }

    var swapState: SwapModule.DataSourceState {
        let exactIn = tradeService.tradeType == .exactIn

        return SwapModule.DataSourceState(
            tokenFrom: tradeService.tokenIn,
            tokenTo: tradeService.tokenOut,
            amountFrom: tradeService.amountIn,
            amountTo: tradeService.amountOut,
            exactFrom: exactIn
        )
    }
}

extension UniswapV3Module {
    struct PriceImpactViewItem {
        let value: String
        let level: UniswapTradeService.PriceImpactLevel
    }

    struct GuaranteedAmountViewItem {
        let title: String
        let value: String?
    }

    enum UniswapWarning: Warning {
        case highPriceImpact
    }

    enum TradeError: Error {
        case wrapUnwrapNotAllowed
    }
}
